// includes
#include "graph.h"

// definitions
EdgeList* createEdgeList()
{
    EdgeList *edgeList = (EdgeList *)malloc(sizeof(EdgeList));
    edgeList->edgeCount = 0;
    edgeList->head = NULL;

    return edgeList;
}

void destroyEdgeList(EdgeList *edgeList)
{
    Edge *edge = NULL;
    Edge *nextEdge = NULL;

    if (edgeList != NULL)
    {
        for (edge = edgeList->head; edge != NULL; edge = nextEdge)
        {
            nextEdge = edge->next;
            destroyEdge(edge);
        }

        free (edgeList);
    }
}

NodeList* createNodeList()
{
    NodeList *nodeList = (NodeList *)malloc(sizeof(NodeList));
    nodeList->nodeCount = 0;
    nodeList->head = NULL;

    return nodeList;
}

void destroyNodeList(NodeList *nodeList)
{
    Node *nextNode = NULL;
    Node *node = NULL;

    if (nodeList != NULL)
    {
        for (node = nodeList->head; node != NULL; node = nextNode)
        {
            nextNode = node->next;
            destroyNode(node);
        }

        free (nodeList);
    }
}

Node* createNode()
{
    Node *node = (Node *)malloc(sizeof(Node));
    node->inwardEdges = createEdgeList();
    node->outwardEdges = createEdgeList();
    node->inbox = createPacketList();
    node->outbox = createPacketList();
    node->data = 0;
    node->next = NULL;
    node->nodeNumber = 0;

    return node;
}

void destroyNode(Node *node)
{
    if (node != NULL)
    {
        if (node->inwardEdges != NULL)
        {
            DESTROY_EDGE_LIST(node->inwardEdges)
        }

        if (node->outwardEdges != NULL)
        {
            DESTROY_EDGE_LIST(node->outwardEdges)
        }

        if (node->inbox != NULL)
        {
            DESTROY_PACKET_LIST(node->inbox)
        }

        if (node->outbox != NULL)
        {
            DESTROY_PACKET_LIST(node->outbox)
        }

        free(node);
    }
}

Edge* createEdge()
{
    Edge *edge = (Edge *)malloc(sizeof(Edge));
    edge->to = 0;
    edge->from = 0;
    edge->next = NULL;

    return edge;
}

void destroyEdge(Edge *edge)
{
    if (edge != NULL)
    {
        free(edge);
    }
}

Graph* createGraph()
{
    Graph *g = (Graph *)malloc(sizeof(Graph));

    g->nodes = createNodeList();;
    g->edges = createEdgeList();
    g->nodeComputation = NULL;

    return g;
}

void destroyGraph(Graph *g)
{
    if (g != NULL)
    {
        if (g->nodes != NULL)
        {
            DESTROY_NODE_LIST(g->nodes);
        }

        if (g->edges != NULL)
        {
            DESTROY_EDGE_LIST(g->edges)
        }

        free(g);
    }
}

void printGraph(Graph *g)
{
    Node *node;
    NodeList *nodes;

    if (g == NULL)
        return;

    nodes = g->nodes;
    if (nodes->head == NULL)
        return;

    for (node = nodes->head; node != NULL; node = node->next)
    {
        printf ("Node number %d, data is %d\n", node->nodeNumber, node->data);
    }
}

void addNode (Graph *g, Node *node)
{
    Node *head;

    if ((g == NULL) || (node == NULL))
        return;

    if (g->nodes == NULL)
    {
        LOG ("g->nodes is NULL. Some thing is wrong\n");
        return;
    }

    g->nodes->nodeCount++;
    head = g->nodes->head;
    node->next = head;
    g->nodes->head = node;
}

Node* findNode (Graph *g, int nodeNumber)
{
    Node *node;
    NodeList *nodes;

    if (g == NULL)
        return NULL;

    nodes = g->nodes;
    if (nodes == NULL)
    {
        LOG ("g->nodes is NULL. Some thing is wrong\n");
        return NULL;
    }

    if (nodes->head == NULL)
        return NULL;

    for (node = nodes->head; node != NULL; node = node->next)
    {
        if (node->nodeNumber == nodeNumber)
            return node;
    }

    return NULL;
}

void addEdgeToList (EdgeList *edgeList, Edge *edge)
{
    if ((edgeList == NULL) || (edge == NULL))
        return;

    edgeList->edgeCount++;
    edge->next = edgeList->head;
    edgeList->head = edge;
}

Edge* dupEdge (Edge *edge)
{
    Edge *newEdge = createEdge();
    
    newEdge->from = edge->from;
    newEdge->to = edge->to;

    return newEdge;
}

void addEdge (Graph *g, Edge *edge)
{
    Node *fromNode, *toNode;

    if ((g == NULL) || (edge == NULL))
        return;

    fromNode = findNode(g, edge->from);
    toNode = findNode(g, edge->to);

    addEdgeToList (toNode->inwardEdges, dupEdge(edge));
    addEdgeToList (fromNode->outwardEdges, dupEdge(edge));
    addEdgeToList (g->edges, edge);
}